package cn.iocoder.mall.shop.service.utils;

/***
 *@author yinxucun
 *@date 2019-12-10 19:24 
 */

import cn.iocoder.common.framework.exception.ServiceException;

import java.util.ArrayList;
import java.util.List;

public class GeoHash {
    private static final double Max_Lat = 90;
    private static final double Min_Lat = -90;
    private static final double Max_Lng = 180;
    private  static final double Min_Lng = -180;
    private static final int length = 20;
    private static final double latUnit = (Max_Lat - Min_Lat) / (1 << 20);
    private  static final double lngUnit = (Max_Lng - Min_Lng) / (1 << 20);
    private  static final String[] base32Lookup =
            {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "b", "c", "d", "e", "f", "g", "h",
                    "j", "k", "m", "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"};

    private static  void convert(double min, double max, double value, List<Character> list) {
        if (list.size() > (length - 1)) {
            return;
        }
        double mid = (max + min) / 2;
        if (value < mid) {
            list.add('0');
            convert(min, mid, value, list);
        } else {
            list.add('1');
            convert(mid, max, value, list);
        }
    }


    private static String base32Encode(final String str) {
        String unit = "";
        StringBuilder sb = new StringBuilder();
        for (int start = 0; start < str.length(); start = start + 5) {
            unit = str.substring(start, start + 5);
            sb.append(base32Lookup[convertToIndex(unit)]);
        }
        return sb.toString();
    }

    private static int convertToIndex(String str) {
        int length = str.length();
        int result = 0;
        for (int index = 0; index < length; index++) {
            result += str.charAt(index) == '0' ? 0 : 1 << (length - 1 - index);
        }
        return result;
    }

    public static String encode(String coordinate) {
            String[] cos=coordinate.split(",");
            if (cos.length!=2){
                throw new ServiceException(1,"坐标信息错误");
            }
            double lat,lng;
            try {
                lat=Double.parseDouble(cos[0]);lng=Double.parseDouble(cos[1]);
            }catch (NumberFormatException e){
                throw  new ServiceException(2,"坐标格式化错误");
            }

        return encode(lat,lng);
    }
        public static String encode(double lat, double lng) {
        List<Character> latList = new ArrayList<Character>();
        List<Character> lngList = new ArrayList<Character>();
        convert(Min_Lat, Max_Lat, lat, latList);
        convert(Min_Lng, Max_Lng, lng, lngList);
            StringBuilder sb = new StringBuilder();
        for (int index = 0; index < latList.size(); index++) {
            sb.append(lngList.get(index)).append(latList.get(index));
        }
        return base32Encode(sb.toString());
    }

    public static List<String> around(double lat, double lng) {
        List<String> list = new ArrayList<String>();
        list.add(encode(lat, lng));
        list.add(encode(lat + latUnit, lng));
        list.add(encode(lat - latUnit, lng));
        list.add(encode(lat, lng + lngUnit));
        list.add(encode(lat, lng - lngUnit));
        list.add(encode(lat + latUnit, lng + lngUnit));
        list.add(encode(lat + latUnit, lng - lngUnit));
        list.add(encode(lat - latUnit, lng + lngUnit));
        list.add(encode(lat - latUnit, lng - lngUnit));
        return list;
    }
    private static String getBase32BinaryString(int i) {
        if (i < 0 || i > 31) {
            return null;
        }
        String str = Integer.toBinaryString(i + 32);
        return str.substring(1);
    }


    public static double[] decode(String geoHash) {
        StringBuilder buffer = new StringBuilder();
        double[] re=new double[2];
        for (char c : geoHash.toCharArray()) {
            buffer.append( getBase32BinaryString(getIndex(c,0,31)));
        }

        List<Character> latList = new ArrayList<Character>();
        List<Character> lngList = new ArrayList<Character>();
        char[] characters=buffer.toString().toCharArray();
        for (int i=0;i<characters.length;i++){
            if(i%2==1){
                latList.add(characters[i]);
            }else {
                lngList.add(characters[i]);
            }
        }
        buffer.delete(0,buffer.length());
        latList.stream().forEach(o->{buffer.append(o);});
        re[0]=getGeoHashMid(buffer.toString(),Min_Lat,Max_Lat);
        buffer.delete(0,buffer.length());
        lngList.stream().forEach(o->{buffer.append(o);});
        re[1]=getGeoHashMid(buffer.toString(),Min_Lng,Max_Lng);
        return re ;
    }
    private static double getGeoHashMid(String binaryStr, double min, double max) {
        if (binaryStr == null || binaryStr.length() < 1) {
            return (min + max) / 2.0;
        }
        if (binaryStr.charAt(0) == '1') {
            return getGeoHashMid(binaryStr.substring(1), (min + max) / 2.0, max);
        } else {
            return getGeoHashMid(binaryStr.substring(1), min, (min + max) / 2.0);
        }
    }
    private static int getIndex(Character c,int min,int max){
        int mid =(min+max)/2;
        if (c.toString().equals(base32Lookup[mid])){
            return mid;
        }
        if (base32Lookup[mid].charAt(0)>c){
            return getIndex(c,min,mid-1);
        }
        if (base32Lookup[mid].charAt(0)<c){
            return getIndex(c,mid+1,max);
        }
        return mid;
    }

    public static void main(String[] args){
        System.out.println(encode("36.49,117.8"));
    }
}
